/**
 * Exam No: Y8467136
 */
package japrc2011;

import java.util.ArrayList;
import java.util.Timer;
import java.util.TimerTask;


public final class Robot implements RobotInterface
{
	private static MapTableModel entire_map = new MapTableModel();
	MapEntity[][] newMapView;
    private long commandCount = 0;
    private String name;
    
    private Status currentStatus = Status.ACTIVE;//The status is an enum in the RobotInterface interface with "ACTIVE, DESTROYED" values.
    
    private GridLocation loc = new GridLocation(0,0);//The new added robots are located in this position

    private ArrayList<GridLocation> waiting_commands= new ArrayList<GridLocation>(); 
    
    int x = loc.getX();
    int y = loc.getY();
    Thread t1;
    int flag = 0;
    
    //values for the repeat!!
    int repeat_value = 0;
    int thetime;
    String thecommand;
    
    //values for the multi-square commands
    Boolean multi_square = false;
    
    //variable for the WAIT command
    int wait_interval = 0;
    boolean wait_is_pressed = false;
    
    //Timer t;
    //TimerTask task1;
    int value = 0;
    boolean thread = false;

    /** CONSTRUCTOR */
    public Robot(String name)
    {
        this.name = name;
    }
    
//---------------------------------------------------GETINFO METHOD-------------------------------------------------
    /**
     * 
     * @param string
     * @return
     */
    public long getInfo(String string)
    {
        if(string.equals("COMMAND_COUNT"))
        {
            return commandCount;
        }
        else
        {
            throw new RobotException("Invalid info item name");
        }
    }

//------------------------------------------------RECEIVECOMMAND METHOD--------------------------------------------
   /**
    * 
    * @param commandString
    */
    public void receiveCommand(String raw_commandString)
    {    	
    	repeat_value = 0; //if the value of this variable is 1, then a REPEAT command has received from the robot.
    	String commandString = checkFormatOfString(raw_commandString);
    	if(getStatus() == Status.DESTROYED)//if the status of the robot is destroyed
    	{
    		//do nothing
    	}
    	else
    	{
    		if (commandString.startsWith("IF"))
    		{
    			//read the condition(take the important part)
    			String[] com = commandString.split(" "); //split the command
    			String condition = com[1].substring(1, com[1].length()-1).trim(); //Get the raw condition. Remove the parenthesis. 
        	
    			//read the command(take the important part)
    			String theifcommand = com[2].substring(1, com[2].length()-1).trim();
    			theifcommand = checkFormatOfString(theifcommand);
        	
    			//these lines does not permit a REPEAT or and if command to be the action of the if command
    			//Another way to implement the if commands is to use *recursion
    			if(theifcommand.startsWith("REPEAT") || theifcommand.startsWith("IF"))
    			{
    				//throw new RobotException("Invalid command string"); //*recursion
    				commandString = "iNvAlId";
    			}
    			else
    			{
    				//check the condition if is true
    				if(condition.equals("EDGE_OF_MAP"))
    				{
    					if( x == entire_map.getColumnCount() || y == entire_map.getRowCount() || x == 0 || y == 0) 
    					{
    						//if the condition is true proceed to the execution of the command  
    						commandString = theifcommand;
    						//receiveCommand(commandString); //*recursion
    					}
    					else
    					{
    						commandString = "TEST";
    						//receiveCommand(commandString); //*recursion
    					}
    				}
    				else if(condition.equals("BLOCKED_NORTH"))
    				{
    					if(blocked_north())
    					{
    						//if the condition is true proceed to the execution of the command  
    						commandString = theifcommand;
    						//receiveCommand(commandString); //*recursion
    					}
    					else
    					{
    						commandString = "TEST";
    						//receiveCommand(commandString); //*recursion
    					}
    				}
    				else if(condition.equals("BLOCKED_SOUTH"))
    				{
    					if(blocked_south())
    					{
    						//if the condition is true proceed to the execution of the command 
    						commandString = theifcommand;
    						//receiveCommand(commandString); //*recursion
    					}
    					else
    					{
    						commandString = "TEST";
    						//receiveCommand(commandString); //*recursion
    					}
    				}
    				else if(condition.equals("BLOCKED_EAST"))
    				{
    					if(blocked_east())
    					{
    						//if the condition is true proceed to the execution of the command 
    						commandString = theifcommand;
    						//receiveCommand(commandString); //*recursion
    					}
    					else 
    					{
    						commandString = "TEST";
    						//receiveCommand(commandString); //*recursion
    					}
    				}
    				else if(condition.equals("BLOCKED_WEST"))
    				{
    					if(blocked_west())
    					{
    						//if the condition is true proceed to the execution of the command 
    						commandString = theifcommand;
    						//receiveCommand(commandString); //*recursion
    					}
    					else
    					{
    						commandString = "TEST";//do nothing
    						//receiveCommand(commandString); //*recursion
    					}
    				}
    				/*else //*recursion
    				{
    					throw new RobotException("Invalid condition string");
    				}*/
    			}
        }
    	/*else*/
    	if(commandString.equals("SELF_DESTRUCT"))// if the SELF_DESTRUCT button is pressed 
        {
    		selfDestructcommand();
        }
        else if (commandString.startsWith("M"))//if a command has been received 
        { 
        	validCommand(commandString);        
        }
        else if(commandString.equals("TEST"))
        {
        	testCommand();
        }
        else if(commandString.startsWith("REPEAT")){
			
        	//read the condition(take the important part)
        	String[] com = commandString.split(" "); 
        	String precommand = com[1].substring(1, com[1].length()-1).trim(); 
        	thecommand = checkFormatOfString(precommand);
        	
        	//these lines ensure that a REPEAT or an if command won't be the action of the REPEAT command
        	if(thecommand.startsWith("REP") || thecommand.startsWith("I"))
        	{
        		throw new RobotException("Invalid command string_1");
        	}
        	else
        	{
        		//read the command(take the important part)
            	try
            	{
            		thetime = Integer.parseInt(com[2].substring(1, com[2].length()-1).trim());
            		if(thetime<0)//checks if the interval of the repeat command is negative
            		{
            			throw new RobotException("Invalid command. Please give a positive integer interval");
            		}
            		else
            		{
            			//receiveCommand(thecommand);
            			repeat_value = 1;//using this variable the RobotSimulator can understand if the robot received a repeat command, so to start repeating the command.
            		}
            	}
            	catch(NumberFormatException exc)//throws this exception when the interval in the repeat command is not an integer
            	{
            		throw new RobotException("Invalid command. Give a positive integer interval");
            	}
        	} 
		}
        else if(commandString.startsWith("WAIT"))
    	{
        	//System.out.println("The current Thread is: " + Thread.currentThread().getName());
        	String com[] = commandString.split(" ");
        	wait_interval = Integer.parseInt(com[1]);//positive integer

        	wait_is_pressed = true;

    	}
        else//in any other case throw an exception that the command(commandString not valid) was invalid.
        {
            throw new RobotException("Invalid command string");
        }
    	}
    }
    //}


//-----------------------------------------------------getStatus METHOD---------------------------------------------
    
    /**
     * returns the current status of the robot
     */
    public Status getStatus()
    {
        return currentStatus;
    }

//----------------------------------------------------GridLocation METHOD-------------------------------------------
    
    /**
     * Returns the current grid location of the robot
     */
    public GridLocation getLocation()
    {
        return loc;
    }
    
//-----------------------------------------------------SetLocation METHOD-------------------------------------------
    
    /**
     * 
     * @param newLoc
     */
    public void setLocation(GridLocation newLoc)
    {
        loc = newLoc; 
    }
    
//-------------------------------------------------------GetName METHOD----------------------------------------------

    /**
     * Returns the name of the robot.
     */
    public String getName()
    {
        return name;
    }

//------------------------------------------------------ifcommand METHOD---------------------------------------------
    
    /**
     * Destroys the Robot. 
     * 1) Sets the status of the robot to destroyed,
     * 2) Clears the list of the commands which the robot has to execute, because as destroyed it can not execute any further commands,
     * 3) Increases the commandCount variable. Because it is a valid command.
     */
    private void selfDestructcommand()
    {
    	currentStatus = Status.DESTROYED; //change the status to destroyed.
    	waiting_commands = null;//the robot should not carry out any further actions (either in response to new commands, or for earlier commands following a tick(), or to any REPEATs that were set up).
        commandCount++;
    }
    
    /**
     * Checks if the robot can move to a specified position, checking all the intermediate positions the robot 
     * has to pass through in order to go to the final position.
     * checks if there are obstacles or not
     * increases the command count for every valid command
     * adds the new valid move in the arrayList
     * @param commandString2
     */
    private void validCommand(String commandString2)
    {
    	String commandString = commandString2;
    	int first_locX = x; //In this variable we save the current X of the robot in the map
    	int first_locY = y; //In this variable we save the current Y of the robot in the map
    	String[] com = commandString.split(" ");//splits the string(separator is the space character)
        
        if(com.length != 3)//if the words after the split are less than three then the command was invalid and the program throws an exception. 
        {
            throw new RobotException("Invalid command string");
        }
        else//else if the words after the split are 3 then we make the appropriate conversions and set the new locations.
        {
            int xChange = Integer.parseInt(com[1]);
            int yChange = Integer.parseInt(com[2]);
            //we change the location of the robot here in order the robot to know if he can move to the next place or not.
            while(xChange!=0 || yChange!=0)
            {
            	int prevX = x;//In this variable we save the new x
            	int prevY = y;//In this variable we save the new y
            	if(xChange>0)
            	{
                    x = x + 1;
                    xChange = xChange - 1;
            	}
            	else if(xChange<0)
            	{
            		x = x - 1;
                    xChange = xChange + 1;
            	}
            	else
            	{
            		
            	}
            	if(yChange>0)
            	{
            		y = y + 1;
                    yChange = yChange - 1;
            	}
            	else if(yChange<0)
            	{
                    y = y - 1;
                    yChange = yChange + 1;
            	}
            	else 
            	{
            		
            	}
            	if( x >= entire_map.getColumnCount() || y >= entire_map.getRowCount() || x < 0 || y < 0 || entire_map.getValueAt(y, x).toString().startsWith("WALL") || (entire_map.getValueAt(y, x).toString().startsWith("ROBOT") && !(entire_map.getValueAt(y, x).toString().equals("ROBOT: " + getName()))))  
            	{
            		x = prevX;
            		y = prevY;
            		break;
            	}
            	else
            	{
            		prevX = x;
            		prevY = y;
            	}
            }
            	commandCount++; // increases the number of commands

            	if(first_locX != x || first_locY != y)
                    waiting_commands.add(new GridLocation((x),(y)));           
        }
        
        //If the robot waits(WAIT) then the robot receives the commands, but does not execute them
        //Using a Thread and join method of the thread we make the program to wait until the thread is finished.
        /*if(wait_is_pressed == true)
        {
        try {
			t1.join();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
        }*/
    }
    
    private void testCommand()
    {
    	commandCount++;
    }
    
//---------------------------------- Methods for checking the next position for obstacles----------------------------
    
    /**
     * Checks if the robot can move to the next-north position, if there is an obstacle or not. 
     * 
     * @return true if there is an obstacle and false if not.
     */
    private boolean blocked_north()
    {
    	boolean bl;
    	y = y - 1; 
    	if( y < 0 || entire_map.getValueAt(y, x).toString().startsWith("WALL") || (entire_map.getValueAt(y, x).toString().startsWith("ROBOT") && !(entire_map.getValueAt(y, x).toString().equals("ROBOT: " + getName())))) 
    	{
    		bl = true;
    	}
    	else 
    		bl = false;
    	y = y + 1;
    	return bl;
    }
    
    /**
     * Checks if the robot can move to the next-south position, if there is an obstacle or not.
     * 
     * @return true if there is an obstacle and false if not.
     */
    private boolean blocked_south()
    {
    	boolean bl;
    	y = y + 1; 
    	if( y >= entire_map.getRowCount() || entire_map.getValueAt(y, x).toString().startsWith("WALL") || (entire_map.getValueAt(y, x).toString().startsWith("ROBOT") && !(entire_map.getValueAt(y, x).toString().equals("ROBOT: " + getName()))))   
    	{
    		bl = true;
    	}
    	else 
    		bl = false;
    	y = y - 1;
    	return bl;
    }
    
    /**
     * Checks if the robot can move to the next-east position, if there is an obstacle or not.
     * 
     * @return true if there is an obstacle and false if not.
     */
    private boolean blocked_east()
    {
    	boolean bl;
    	x = x + 1; 
    	if( x >= entire_map.getColumnCount() || entire_map.getValueAt(y, x).toString().startsWith("WALL") || (entire_map.getValueAt(y, x).toString().startsWith("ROBOT") && !(entire_map.getValueAt(y, x).toString().equals("ROBOT: " + getName())))) 
    	{
    		bl = true;
    	}
    	else 
    		bl = false;
    	x = x - 1;
    	return bl;
    }
    
    /**
     * Checks if the robot can move to the next-west position, if there is an obstacle or not.
     * 
     * @return true if there is an obstacle and false if not.
     */
    private boolean blocked_west()
    {
    	boolean bl;
    	x = x - 1; 
    	if( x < 0 || entire_map.getValueAt(y, x).toString().startsWith("WALL") || (entire_map.getValueAt(y, x).toString().startsWith("ROBOT") && !(entire_map.getValueAt(y, x).toString().equals("ROBOT: " + getName()))))  
    	{
    		bl = true;
    	}
    	else 
    		bl = false;
    	x = x + 1;
    	return bl;
    }
    
//----------------------------------------------------End of BLOCKED Methods-----------------------------------------
    
    /**
     * 
     */
    @Override
    public void tick()
    {
    	if(waiting_commands == null || waiting_commands.isEmpty())
    	{
    		
    	} 
    	else 
    	{
    		//System.out.println(waiting_commands.get(0));
    		String var = String.valueOf(waiting_commands.get(0)); //always read the first element of the ArrayList.
    		int len = var.length();
    		String var2 = var.substring(1,len-1); //removes the parentheses from the 1st element of the ArrayList. 
    		String[] var3 = var2.split(","); //split using the character "," and put the values in an array
    		loc.setX( Integer.parseInt(var3[0]) ); //sets the new x 
    		loc.setY( Integer.parseInt(var3[1]) ); //sets the new y
    		waiting_commands.remove(0); //delete the 1st element of the ArrayList after reading and using that! Only the non executed commands remain in the map.
    	}
    }

    
    /**
     * Receives the state of the current map
     * 
     * @param map
     */
	public void receiveMap(MapEntity[][] map) 
	{ //--
		entire_map = new MapTableModel();
        newMapView = map;//takes all the objects that included in the map(returned from the getMapState();) and saves them in the newMapView variable
        for(int i=0; i< entire_map.getColumnCount(); ++i) { 
            for(int j=0; j< entire_map.getRowCount(); ++j) { 
                //translate from the (col,row) view used by the sim to the (row,col) view used by JTable
                MapEntity m = newMapView[i][j]; 
                if(m==null) 
                {
                    entire_map.setValueAt("", j, i); 
                } 
                else 
                { 
                	entire_map.setValueAt(m, j, i);
                } 
            } 
            
        } 
	} 
	
	
	/**
	 * Method which checks if the thread for the wait command is running
	 * If it is running then this method interrupts the execution of the thread.
	 */
	public void stopW_R()
	{
		if(wait_is_pressed == true)
		{
			wait_is_pressed = false;
			t1.interrupt();
		}
	}
	
	//this method is used to convert the command in an acceptable format(i.e. remove the irrelevant white spaces
    public String checkFormatOfString(String str) 
    { 
		String str2 = str.replace(" ", "");
		if (str2.startsWith("WAIT")) 
		{
			str2 = str2.substring(0, 4).concat(" ").concat(str2.substring(4));
		}
		else if (str2.startsWith("M")) 
		{ 
			if(str2.length() == 3) 
			{ 
				str2 = String.valueOf(str2.charAt(0) + " " + str2.charAt(1) + " " + str2.charAt(2)); 
			} 
			else if(str2.length() == 4) 
			{ 
				if(str2.charAt(1) == '-') 
				{ 
					str2 = String.valueOf(str2.charAt(0) + " " + str2.charAt(1) + str2.charAt(2) + " " + str2.charAt(3)); 
				} 
				else if(str2.charAt(2) == '-') 
				{ 
					str2 = String.valueOf(str2.charAt(0) + " " + str2.charAt(1) + " " + str2.charAt(2) + str2.charAt(3)); 
				} 
			} 
			else 
			{ 
				System.err.println("Invalid entry!!!"); 
			} 
		} 
		else if(str2.startsWith("IF") || str2.startsWith("REPEAT")) 
		{
			//String delimeter = "\\(";
			String[] st = str2.split("\\(");
			str2 = null;
			for(int sp=0; sp<st.length; sp++)
			{
				if(sp==0)
				{
				str2 = st[sp];
				str2 = str2.concat(" ");
				}
				else if(sp==1)
				{
					str2 = str2.concat("(");
					str2 = str2.concat(st[sp]);
					str2 = str2.concat(" ");
				}
				else if(sp==1)
				{
					str2 = str2.concat("(");
					str2 = str2.concat(st[sp]);
					str2 = str2.concat(" ");
				}
				else if(sp==2)
				{
					str2 = str2.concat("(");
					str2 = str2.concat(st[sp]);
				}
			}
			
			str2.trim();
		}
		return str2;   	
    } 

}
